﻿using JuCheap.Core.Infrastructure.Extentions;
using JuCheap.Core.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace JuCheap.Core.WebApi.Controllers
{
    /// <summary>
    /// 文件上传api
    /// </summary>
    [Route("api/file")]
    public class FileUploadController : BaseApiController
    {
        private readonly ILogger<FileUploadController> _logger; 
        private readonly IWebHostEnvironment _webHostEnvironment;

        /// <summary>
        /// 文件存放的根路径
        /// </summary>
        private readonly string _baseUploadDir;
        /// <summary>
        /// 图片地址基础url
        /// </summary>
        private readonly string _baseUrl;

        /// <summary>
        /// ctor
        /// </summary>
        public FileUploadController(ILogger<FileUploadController> logger,
            IWebHostEnvironment hostEnvironment,
            IConfiguration configuration)
        {
            _logger = logger;
            _webHostEnvironment = hostEnvironment;
            _baseUploadDir = string.Concat(_webHostEnvironment.ContentRootPath, "\\wwwroot\\uploads\\");
            _baseUrl = configuration["urls"].TrimEnd('/');
        }

        /// <summary>
        /// 图片上传
        /// </summary>
        [HttpPost]
        [Route("upload-image")]
        public async Task<ActionResult<FileResponseDto>> UploadImage()
        {
            var yearMonth = DateTime.Now.ToString("yyyyMM");
            //表单中取得文件
            var file = Request.Form.Files["file"];
            //最终的文件名（demo中保存的是它上传时候的文件名，实际操作肯定不能这样）
            var filePath = $"{_baseUploadDir}\\{yearMonth}\\";
            filePath.CreateDirectoryIfNotExists();
            var extension = file.FileName.GetExt();
            var newFileName = $"{Guid.NewGuid().ToString("N")}.{extension}";
            var filePathWithFileName = string.Concat(filePath, newFileName);
            
            using (var stream = new FileStream(filePathWithFileName, FileMode.Create))
            {
                await file.CopyToAsync(stream);
                await stream.FlushAsync();
                stream.Close();
            }
            var url = $"{_baseUrl}/uploads/{yearMonth}/{newFileName}";
            var fileResponseDto = new FileResponseDto();
            fileResponseDto.Completed = true;
            fileResponseDto.FileUrl = url;
            fileResponseDto.FileName = newFileName;
            return Ok(fileResponseDto);
        }

        /// <summary>
        /// 分片上传文件
        /// </summary>
        [HttpPost]
        [Route("upload")]
        public async Task<ActionResult<FileResponseDto>> Upload()
        {
            var fileName = Request.Form["name"];
            //当前分块序号
            var index = Request.Form["chunk"].ToString().ToInt();
            //所有块数
            var maxChunk = Request.Form["maxChunk"].ToString().ToInt();
            //前端传来的GUID号
            var guid = Request.Form["guid"];
            //临时保存分块的目录
            var dir = Path.Combine(_baseUploadDir, guid);
            dir.CreateDirectoryIfNotExists();
            //分块文件名为索引名，更严谨一些可以加上是否存在的判断，防止多线程时并发冲突
            var filePath = Path.Combine(dir, index.ToString());
            //表单中取得分块文件
            var file = Request.Form.Files["file"];
            //获取文件扩展名
            //var extension = file.FileName.Substring(file.FileName.LastIndexOf(".") + 1, (file.FileName.Length - file.FileName.LastIndexOf(".") - 1));
            var filePathWithFileName = string.Concat(filePath, fileName);
            using (var stream = new FileStream(filePathWithFileName, FileMode.Create))
            {
                await file.CopyToAsync(stream);
                await stream.FlushAsync();
                stream.Close();
            }

            //如果是最后一个分块， 则合并文件
            var fileResponseDto = new FileResponseDto();
            if (index == maxChunk - 1)
            {
                await MergeFileAsync(fileName, guid);
                fileResponseDto.Completed = true;
            }
            return Ok(fileResponseDto);
        }

        /// <summary>
        /// 合并分片的文件
        /// </summary>
        /// <param name="fileName">文件名称</param>
        /// <param name="guid">文件guid</param>
        private async Task MergeFileAsync(string fileName, string guid)
        {
            //临时文件夹
            var dir = Path.Combine(_baseUploadDir, guid);
            //获得下面的所有文件
            var files = Directory.GetFiles(dir);
            var yearMonth = DateTime.Now.ToString("yyyyMM");
            //最终的文件名（demo中保存的是它上传时候的文件名，实际操作肯定不能这样）
            var finalDir = Path.Combine(_baseUploadDir, yearMonth, guid);
            finalDir.CreateDirectoryIfNotExists();
            var finalPath = Path.Combine(finalDir, fileName);
            using (var fs = new FileStream(finalPath, FileMode.Create))
            {
                //排一下序，保证从0-N Write
                var fileParts = files.OrderBy(x => x.Length).ThenBy(x => x);
                foreach (var part in fileParts)
                {
                    var bytes = await System.IO.File.ReadAllBytesAsync(part);
                    await fs.WriteAsync(bytes, 0, bytes.Length);
                    bytes = null;
                    //删除分块
                    System.IO.File.Delete(part);
                }
                await fs.FlushAsync();
                fs.Close();
                //删除临时文件夹和分片文件
                Directory.Delete(dir);
            }
        }
    }
}
